--[[
@module max30102
@summary max30102 血氧心率传感器
@version 1.0
@date    2022.11.11
@author  wwwxxxzzz666，参考手册及已有相关的驱动资料编写
@usage
待进一步编写
]] local max30102 = {}
local sys = require "sys"
local i2cid -- 用于本库里所有函数使用

MAX30102_ADDRESS = 0x57 -- Slave ID

---器件所用地址
local REG_INTR_STATUS_1 = 0x00 -- 中断状态地址1
local REG_INTR_STATUS_2 = 0x01 -- 中断状态地址2

local REG_INTR_ENABLE_1 = 0x02 -- 中断启用地址1  默认除了电源状态外所有中断都是停用的
local REG_INTR_ENABLE_2 = 0x03 -- 中断启用地址2

local REG_FIFO_WR_PTR = 0x04 -- 存放下次max30102写入样本数据的FIFO地址
local REG_OVF_COUNTER = 0x05 -- 表示丢失的样本数，FIFO满了不会再写入！
local REG_FIFO_RD_PTR = 0x06 -- 存放I2C下一次读取数据所在FIFO中的地址
-- 循环FIFO深度32宽度3或6bytes，取决于采样通道数(1或2，RED在前IR在后)。
-- 每一通道数据3字节，18bit，左对齐
local REG_FIFO_DATA = 0x07 -- 8bit数据
local REG_FIFO_CONFIG = 0x08 -- [7:5]->SAM_AVE[2:0],[4]->FIFO_ROLLOVER_EN,[3:0]->FIFO_A_FULL

local REG_MODE_CONFIG = 0x09 -- [7]->SHDN(省电模式),[6]->RESET(回复上电状态),[2:0]->MODE[2:0]
-- MODE[2:0]:010(HR mode,red only),011(SpO2 mode,red and IR),111(Muti-LED mode,red and IR)
local REG_SPO2_CONFIG = 0x0A -- [6:5]->SPO2_ADC_RGE[1:0],[4:2]->SPO2_SR[2:0],[1:0]->LED_PW[1:0]

local REG_LED1_PA = 0x0C -- Red电流
local REG_LED2_PA = 0x0D -- IR电流
local REG_PILOT_PA = 0x10 -- set the LED power during the proximity mode, as well as in Multi-LED mode.

-- 将一个sample划分为最多4个time slots进行自定义测量，详情参见说明书
local REG_MULTI_LED_CTRL1 = 0x11
local REG_MULTI_LED_CTRL2 = 0x12

local REG_TEMP_INTR = 0x1F -- 温度整数部分，补码形式
local REG_TEMP_FRAC = 0x20 -- 小数部分，最小单位为0.0625℃，一个正向偏移量
local REG_TEMP_CONFIG = 0x21 -- 设1引发测量单个温度数据，读取后自动回0

local REG_PROX_INT_THRESH = 0x30 -- 该寄存器设置将触发HR或SpO2模式开始的IR ADC计数，8bit MSBs

local REG_REV_ID = 0xFE
local REG_PART_ID = 0xFF

local hamm_par = {41, 276, 512, 276, 41}; -- 汉明窗参数
local spo2_table = {}

local NN = 200 -- 一批血氧数据的原始数据采样数
local ODD_BATCH = 4 -- 消除奇异点批次组数据
local MINUS_POINT = 3 -- 第一次（差分前）滤波消去的数据点数（e.g.四点滤波消去三个数据点）
local MINUS_POINT2 = 5 -- 第一次（差分前）滤波消去的数据点数+1（一阶差分消去的数据点）（e.g.两点滤波消去(MINUS_POINT2-MINUS_POINT-1)个数据点）
local HAMM_SIZE = 5 -- 汉明窗尺寸

--[[
max30102设置参数启动测试
@api max30102.setup(i2c_id)
@number i2c_id i2c_id
@return bool   成功返回true
@usage
max30102.setup(0)
]]
function max30102.setup(i2c_id)
    i2cid = i2c_id
    sys.wait(10) -- 10毫秒等待设备稳定

    log.info("max30102_setup", i2cid)

    -- for i = 0, 255, 1 do
    --    local tt=i2c.send(i2c_id,i,0xFF)
    --    log.info("max30102_setup_ID",i,tt)
    --    if tt==true then
    --        local partid_raw=i2c.recv(i2c_id,i,1)
    --        log.info("part_id",partid_raw)
    --    end
    -- end

    i2c.send(i2cid, MAX30102_ADDRESS, {REG_INTR_ENABLE_1, 0xE0})
    -- i2c.writeReg(i2cid,MAX30102_ADDRESS,REG_INTR_ENABLE_1,0xE0) --启用A_FULL，PPG_RDY，ALC_OVF
    i2c.send(i2cid, MAX30102_ADDRESS, {REG_INTR_ENABLE_2, 0x00})
    -- i2c.writeReg(i2cid,MAX30102_ADDRESS,REG_INTR_ENABLE_2,0x00)

    i2c.send(i2cid, MAX30102_ADDRESS, {REG_FIFO_WR_PTR, 0x00})
    -- i2c.writeReg(i2cid,MAX30102_ADDRESS,REG_FIFO_WR_PTR,0x00)
    i2c.send(i2cid, MAX30102_ADDRESS, {REG_FIFO_RD_PTR, 0x00})
    -- i2c.writeReg(i2cid,MAX30102_ADDRESS,REG_FIFO_RD_PTR,0x00)
    i2c.send(i2cid, MAX30102_ADDRESS, {REG_FIFO_WR_PTR, 0x00})
    -- i2c.writeReg(i2cid,MAX30102_ADDRESS,REG_FIFO_WR_PTR,0x00)

    -- LED1 ~7mA
    i2c.send(i2cid, MAX30102_ADDRESS, {REG_LED1_PA, 0x24})
    -- i2c.writeReg(i2cid,MAX30102_ADDRESS,REG_LED1_PA,0x24)
    -- LED2 ~7mA
    i2c.send(i2cid, MAX30102_ADDRESS, {REG_LED2_PA, 0x24})
    -- i2c.writeReg(i2cid,MAX30102_ADDRESS,REG_LED2_PA,0x24)

    -- PILOT_PA待增加
    -- i2c.send(i2cid,MAX30102_ADDRESS,{REG_PILOT_PA,0x7f})

    -- 样本平均采样容量4，fifo_roll_over关闭，fifo容量警告17
    -- i2c.send(i2cid,MAX30102_ADDRESS,{REG_FIFO_CONFIG,0x5f})
    i2c.send(i2cid, MAX30102_ADDRESS, {REG_FIFO_CONFIG, 0x2f})
    -- i2c.writeReg(i2cid,MAX30102_ADDRESS,REG_FIFO_CONFIG,0x4f)
    -- SPO2_ADC range = 4096nA, SPO2 sample rate = 100Hz, LED pulse-width = 411us
    i2c.send(i2cid, MAX30102_ADDRESS, {REG_SPO2_CONFIG, 0x27})
    -- i2c.writeReg(i2cid,MAX30102_ADDRESS,REG_SPO2_CONFIG,0x27)
    -- SpO2模式
    i2c.send(i2cid, MAX30102_ADDRESS, {REG_MODE_CONFIG, 0x03})
    -- i2c.writeReg(i2cid,MAX30102_ADDRESS,REG_MODE_CONFIG,0x03)

    log.info("max30102 setup_ok")

    i2c.send(i2cid, MAX30102_ADDRESS, REG_INTR_STATUS_1)
    local intr_status_raw1 = i2c.recv(i2cid, MAX30102_ADDRESS, 1)
    log.info("max30102.setup_intr1", intr_status_raw1)
    i2c.send(i2cid, MAX30102_ADDRESS, REG_INTR_STATUS_2)
    local intr_status_raw2 = i2c.recv(i2cid, MAX30102_ADDRESS, 1)
    log.info("max30102.setup_intr2", intr_status_raw2)

    return true
end

--[[
max30102重置恢复初始化
@api max30102.reset(i2c_id)
@number i2c_id i2c_id
@return bool   成功返回true
@usage
max30102.reset(0)
]]
function max30102.reset(i2c_id)
    i2cid = i2c_id
    sys.wait(10) -- 10毫秒等待设备稳定
    i2c.send(i2cid, MAX30102_ADDRESS, {REG_MODE_CONFIG, 0x40})
    -- i2c.writeReg(i2cid,MAX30102_ADDRESS,REG_MODE_CONFIG,0x40)
    log.info("max30102 reset_ok")
    return true
end

--[[
max30102进入低功耗模式（关闭数据采集）
@api max30102.shutdown(i2c_id)
@number i2c_id i2c_id
@return bool   成功返回true
@usage
max30102.shutdown(0)
]]
function max30102.shutdown(i2c_id)
    i2cid = i2c_id
    sys.wait(10) -- 10毫秒等待设备稳定
    i2c.send(i2cid, MAX30102_ADDRESS, {REG_MODE_CONFIG, 0x80})
    -- i2c.writeReg(i2cid,MAX30102_ADDRESS,REG_MODE_CONFIG,0x80)
    log.info("max30102 shutdown_ok")
    return true
end

--[[
max30102获取中断状态信息
@api max30102.get_intr()
@return table  intr_data中断数据
@usage
intr_data=max30102.get_intr()
]]
function max30102.get_intr()
    local intr_data = {
        A_FULL = nil,
        PPG_RDY = nil,
        ALC_OVF = nil,
        DIE_TEMP_RDY = nil
    }
    i2c.send(i2cid, MAX30102_ADDRESS, REG_INTR_STATUS_1)
    local intr_status_raw1 = i2c.recv(i2cid, MAX30102_ADDRESS, 1)
    -- local intr_status_raw1=i2c.readReg(i2cid,MAX30102_ADDRESS,REG_INTR_STATUS_1,1)
    local _, intr_status1 = pack.unpack(intr_status_raw1, "b")
    -- log.info("max30102.get_intr_intr_status_raw1",intr_status_raw1)

    i2c.send(i2cid, MAX30102_ADDRESS, REG_INTR_STATUS_2)
    local intr_status_raw2 = i2c.recv(i2cid, MAX30102_ADDRESS, 1)
    -- local intr_status_raw2=i2c.readReg(i2cid,MAX30102_ADDRESS,REG_INTR_STATUS_2,1)
    local _, intr_status2 = pack.unpack(intr_status_raw2, "b")
    -- log.info("max30102.get_intr_intr_status2",intr_status2)
    intr_data.A_FULL = bit.band(bit.lshift(intr_status1, 7), 1)
    intr_data.PPG_RDY = bit.band(bit.lshift(intr_status1, 6), 1)
    intr_data.ALC_OVF = bit.band(bit.lshift(intr_status1, 5), 1)
    intr_data.DIE_TEMP_RDY = bit.band(bit.lshift(intr_status2, 1), 1)
    return intr_data
end

--[[
max30102获取光反射数据
@api max30102.get_pre_data()
@return table  pre_data原始LED反射数据
@usage
pre_data=max30102.get_pre_data()
]]
function max30102.get_pre_data()
    local pre_data = {
        red = nil,
        ir = nil
    }
    local intr_data = max30102.get_intr()
    -- log.info("max30102_pre_data_intr",intr_data.A_FULL,intr_data.ALC_OVF,intr_data.DIE_TEMP_RDY,intr_data.PPG_RDY)
    if (intr_data.ALC_OVF == 1) then
        log.info("ALC_OVF")
    end
    if (intr_data.A_FULL == 1) then
        log.info("A_FULL")
    end
    i2c.send(i2cid, MAX30102_ADDRESS, REG_FIFO_DATA)
    local data_raw = i2c.recv(i2cid, MAX30102_ADDRESS, 6)
    -- local data_raw=i2c.readReg(i2cid,MAX30102_ADDRESS,REG_FIFO_DATA,6)
    local _, data1, data2, data3, data4, data5, data6 = pack.unpack(data_raw, "b6")
    pre_data.ir = bit.band(bit.bor(bit.lshift(data1, 16), bit.lshift(data2, 8), data3), 0x03ffff)
    pre_data.red = bit.band(bit.bor(bit.lshift(data4, 16), bit.lshift(data5, 8), data6), 0x03ffff)
    return pre_data
end

--[[
max30102获取温度数据
@api max30102.get_temp_data()
@return number temp_data原始温度数据
@usage
temp_data=max30102.get_temp_data()
]]
function max30102.get_temp_data()
    local temp_data = nil
    local intr_data = max30102.get_intr()
    -- log.info("max30102_pre_data_intr",intr_data.A_FULL,intr_data.ALC_OVF,intr_data.DIE_TEMP_RDY,intr_data.PPG_RDY)
    if (intr_data.ALC_OVF == 1) then
        log.info("ALC_OVF")
    end
    if (intr_data.A_FULL == 1) then
        log.info("A_FULL")
    end

    i2c.send(i2cid, MAX30102_ADDRESS, {REG_TEMP_CONFIG, 0x01})

    i2c.send(i2cid, MAX30102_ADDRESS, REG_TEMP_INTR)
    local data_raw = i2c.recv(i2cid, MAX30102_ADDRESS, 1)
    local _, data1 = pack.unpack(data_raw, "b")
    -- log.debug('temp_data',data_raw,_,data1)
    i2c.send(i2cid, MAX30102_ADDRESS, REG_TEMP_FRAC)
    local data_raw = i2c.recv(i2cid, MAX30102_ADDRESS, 1)
    local _, data2 = pack.unpack(data_raw, "b")
    temp_data = data1 + 0.0625 * data2
    return temp_data
end

function table_maxn(t)
    local mn = nil;
    for k, v in pairs(t) do
        if (mn == nil) then
            mn = v
        end
        if mn < v then
            mn = v
        end
    end
    return mn
end

function table_minn(t)
    local mn = nil;
    for k, v in pairs(t) do
        if (mn == nil) then
            mn = v
        end
        if mn > v then
            mn = v
        end
    end
    return mn
end

--[[
max30102计算心率与血氧
@api max30102.get_rate_and_spo2()
@return table  心率与血氧
@usage
pre_data=max30102.get_rate_and_spo2()
]]
function max30102.get_rate_and_spo2()
    local result = {
        valid = nil,
        spo2 = nil,
        rate = nil,
        temp = nil
    }
    local ir_r = {}
    local red_r = {} -- 各传感器采集的一批原始数据和处理数据
    local temp_r = {}
    local i = 1
    while i <= NN do
        local pre_data = max30102.get_pre_data()
        if (i > 1) and (pre_data.ir == ir_r[i - 1]) and (pre_data.red == red_r[i - 1]) then
            -- log.debug("data.ignore",i)
            goto continue2
        end
        if pre_data.ir ~= "nil" then
            table.insert(ir_r, pre_data.ir)
        end
        if pre_data.red ~= "nil" then
            table.insert(red_r, pre_data.red)
        end
        local temp_data = max30102.get_temp_data()
        table.insert(temp_r, temp_data)
        i = i + 1
        ::continue2::
    end
    result.valid = 1

    local tsum = 0
    for i = 1, NN do
        tsum = tsum + temp_r[i]
    end
    result.temp = tsum / NN

    if dbg == 1 and network == 1 then
        send_data("temp_r", temp_r, NN)
    end

    temp_r = nil

    if dbg == 1 and network == 1 then
        sys.waitUntil('temp_r', 1000)
        send_data("ir_r", ir_r, NN)
        -- send_data("red_r",red_r,NN)
    end

    log.debug("data_raw", ir_r[NN], ir_r[NN - 1], ";;", red_r[NN], red_r[NN - 1])

    -- 消除奇异点
    local batches = NN // ODD_BATCH
    for i = 1, batches do
        local tsum = 0
        local ir_mean = 0
        for j = 1, ODD_BATCH do
            tsum = tsum + ir_r[(i - 1) * ODD_BATCH + j]
        end
        ir_mean = tsum // ODD_BATCH
        for j = 1, ODD_BATCH do
            if math.abs(ir_r[(i - 1) * ODD_BATCH + j] - ir_mean) > 10 then
                ir_r[(i - 1) * ODD_BATCH + j] = (tsum - ir_r[(i - 1) * ODD_BATCH + j]) // (ODD_BATCH - 1)
            end
        end
    end

    for i = 1, batches do
        local tsum = 0
        local red_mean = 0
        for j = 1, ODD_BATCH do
            tsum = tsum + red_r[(i - 1) * ODD_BATCH + j]
        end
        red_mean = tsum // ODD_BATCH
        for j = 1, ODD_BATCH do
            if math.abs(red_r[(i - 1) * ODD_BATCH + j] - red_mean) > 20 then
                red_r[(i - 1) * ODD_BATCH + j] = (tsum - red_r[(i - 1) * ODD_BATCH + j]) // (ODD_BATCH - 1)
            end
        end
    end

    -- 移除ir的直流分量
    local ir_p = {};
    local ir_mean = 0
    for i = 1, NN do
        ir_mean = ir_mean + ir_r[i]
    end
    ir_mean = ir_mean // NN
    for i = 1, NN do
        ir_p[i] = ir_r[i] - ir_mean
    end

    if (ir_mean < 60000) then
        result.valid = 0
    end

    -- 对IR数据进行四点平滑滤波
    for i = 1, NN - MINUS_POINT do
        local tsum = 0
        for j = 0, MINUS_POINT do
            tsum = tsum + ir_p[i + j]
        end
        ir_p[i] = tsum // (MINUS_POINT + 1)
    end

    if dbg == 1 and network == 1 then
        sys.waitUntil('ir_r', 1000)
        send_data("ir_p", ir_p, NN - MINUS_POINT)
    end

    -- 对IR数据求一阶差分
    local ir_d = {}; -- 差分数据集
    for i = 1, NN - MINUS_POINT - 1 do
        ir_d[i] = ir_p[i + 1] - ir_p[i]
        if (i > 1) and (ir_d[i] < 0) and (ir_d[i - 1] < 0) then
            ir_d[i] = ir_d[i] + ir_d[i - 1]
            ir_d[i - 1] = 0
        end
        if (ir_d[i] < -10000) then
            ir_d[i] = -10000
        end
        if (ir_d[i] > 10000) then
            ir_d[i] = 10000
        end
    end
    -- ir_d[NN-MINUS_POINT-1]=ir_p[NN]-ir_p[NN-1]
    ir_p = nil

    -- 差分后进行第二次两点平滑滤波
    for i = 1, NN - MINUS_POINT2 do
        local tsum = 0
        for j = 0, (MINUS_POINT2 - MINUS_POINT - 1) do
            tsum = tsum + ir_d[i + j]
        end
        ir_d[i] = tsum // (MINUS_POINT2 - MINUS_POINT - 1)
    end

    if dbg == 1 and network == 1 then
        sys.waitUntil('ir_p', 1000)
        send_data("ir_d", ir_d, NN - MINUS_POINT2)
    end

    -- 汉明窗处理
    for i = 1, NN - MINUS_POINT2 - HAMM_SIZE do
        local tsum = 0
        for j = i, i + HAMM_SIZE - 1 do
            -- log.debug("hamm",i,j,tsum,ir_d[j],hamm_par[j-i+1])
            tsum = tsum - ir_d[j] * hamm_par[j - i + 1]
        end
        ir_d[i] = tsum // 1146
    end

    if dbg == 1 and network == 1 then
        sys.waitUntil('ir_d', 1000)
        send_data("ir_d2", ir_d, NN - MINUS_POINT2 - HAMM_SIZE)
    end

    -- 计算阈值
    local nth1 = 0
    for i = 1, NN - MINUS_POINT2 - HAMM_SIZE do -- ?
        nth1 = nth1 + math.abs(ir_d[i])
    end
    nth1 = nth1 // (NN - MINUS_POINT2 - HAMM_SIZE) * 2.5
    -- log.debug("nth$$$$$$$$$$$$$$$$$$$",nth1)

    -- 找峰值位置
    local npeaks, max_peak_locs = find_max_peaks(ir_d, NN - MINUS_POINT2 - HAMM_SIZE, nth1, 15, 5) -- peak_height, peak_distance, max_num_peaks
    ir_d = nil

    -- 求心率
    local n_peak_interval_sum = 0
    if npeaks >= 2 then
        for i = 1, npeaks - 1 do
            log.debug("cal_rate", i, npeaks, n_peak_interval_sum, max_peak_locs[i], max_peak_locs[i + 1])
            n_peak_interval_sum = n_peak_interval_sum + (max_peak_locs[i + 1] - max_peak_locs[i])
        end
        n_peak_interval_sum = n_peak_interval_sum // (npeaks - 1)
        result.rate = 3600 / n_peak_interval_sum
    else
        result.valid = 0
        result.rate = -1
        result.spo2 = -1
        if network == 1 then
            sys.waitUntil('ir_d2', 1000)
        end
        collectgarbage("collect")
        return result
    end

    collectgarbage("collect")

    -- 找波形谷底位置
    local min_valley_locs = {}
    for i = 1, npeaks - 1 do
        min_valley_locs[i] = (max_peak_locs[i] + max_peak_locs[i + 1]) >> 1
    end
    min_valley_locs[npeaks] = max_peak_locs[npeaks] + (HAMM_SIZE >> 1)

    -- 找精确谷底位置
    local n_exact_ir_valley_locs_count = 0
    local exact_ir_valley_locs = {}
    for i = 1, npeaks do
        local bottom_count = 0 -- 标记谷底出现次数
        local cur_pos = min_valley_locs[i] -- 当前找谷底区间的中心位置
        local tmin = 1 << 20
        local tpos = 0
        if (cur_pos + 5 <= NN - MINUS_POINT2 - HAMM_SIZE) and (cur_pos > 5) then
            for j = cur_pos - 4, cur_pos + 4 do
                if (ir_r[j] <= ir_r[j - 1]) and (ir_r[j] <= ir_r[j + 1]) then
                    if ir_r[j] < tmin then
                        tmin = ir_r[j];
                        bottom_count = bottom_count + 1;
                        tpos = j;
                    end
                end
            end
        end
        if bottom_count >= 1 then
            n_exact_ir_valley_locs_count = n_exact_ir_valley_locs_count + 1
            exact_ir_valley_locs[n_exact_ir_valley_locs_count] = tpos
        end
    end

    if n_exact_ir_valley_locs_count < 2 then
        result.valid = 0
        result.spo2 = -1
        result.rate = -1
        if network == 1 then
            sys.waitUntil('ir_d2', 1000)
        end
        collectgarbage("collect")
        return result
    end

    -- 进行四点平滑滤波
    local ir_p2 = {} -- 求SpO2时处理的数据
    local red_p2 = {}
    for i = 1, NN - MINUS_POINT do
        local tsum1 = 0
        local tsum2 = 0
        for j = 0, MINUS_POINT do
            tsum1 = tsum1 + ir_r[i + j]
            tsum2 = tsum2 + red_r[i + j]
        end
        ir_p2[i] = tsum1 // (MINUS_POINT + 1)
        red_p2[i] = tsum2 // (MINUS_POINT + 1)
    end
    ir_r = nil;
    red_r = nil;

    local n_ratio_average = 0
    local n_i_ratio_count = 0
    local an_ratio = {0, 0, 0, 0, 0}
    for i = 1, n_exact_ir_valley_locs_count - 1 do
        if exact_ir_valley_locs[i + 1] - exact_ir_valley_locs[i] > 10 then
            local ir_max = -(1 << 20)
            local red_max = -(1 << 20)
            local ir_max_pos = 0
            local red_max_pos = 0
            for j = exact_ir_valley_locs[i], exact_ir_valley_locs[i + 1] do
                if ir_p2[j] > ir_max then
                    ir_max = ir_p2[j];
                    ir_max_pos = j;
                end
                if red_p2[j] > red_max then
                    red_max = red_p2[j];
                    red_max_pos = j;
                end
            end
            local red_ac = (red_p2[exact_ir_valley_locs[i + 1]] - red_p2[exact_ir_valley_locs[i]]) *
                               (red_max_pos - exact_ir_valley_locs[i])
            red_ac = red_p2[exact_ir_valley_locs[i]] + red_ac // (exact_ir_valley_locs[i + 1] - exact_ir_valley_locs[i])
            red_ac = red_p2[red_max_pos] - red_ac
            local ir_ac = (ir_p2[exact_ir_valley_locs[i + 1]] - ir_p2[exact_ir_valley_locs[i]]) *
                              (ir_max_pos - exact_ir_valley_locs[i])
            ir_ac = ir_p2[exact_ir_valley_locs[i]] + ir_ac // (exact_ir_valley_locs[i + 1] - exact_ir_valley_locs[i])
            ir_ac = ir_p2[ir_max_pos] - ir_ac
            local n_nume = (red_ac * ir_max) >> 7
            local n_denom = (ir_ac * red_max) >> 7
            if (n_denom > 0) and (n_i_ratio_count < 5) and (n_nume ~= 0) then
                n_i_ratio_count = n_i_ratio_count + 1
                an_ratio[n_i_ratio_count] = (n_nume * 100) // n_denom
            end
        end
    end
    ir_p2 = nil;
    red_p2 = nil;

    -- 对an_ratio作一下升序排序
    for i = 1, n_i_ratio_count - 1 do
        for j = i + 1, n_i_ratio_count do
            if an_ratio[i] > an_ratio[j] then
                local t = an_ratio[i]
                an_ratio[i] = an_ratio[j]
                an_ratio[j] = t
            end
        end
    end
    -- sort_ascend(an_ratio,n_i_ratio_count)

    local n_mid_pos = (n_i_ratio_count >> 1) + 1

    if n_mid_pos > 1 then
        n_ratio_average = (an_ratio[n_mid_pos - 1] + an_ratio[n_mid_pos]) >> 1
    else
        n_ratio_average = an_ratio[n_mid_pos]
    end

    if (n_ratio_average > 2) and (n_ratio_average < 184) then
        -- result.spo2=spo2_table[n_ratio_average]
        n_ratio_average = n_ratio_average / 100
        result.spo2 = (-45.060) * n_ratio_average * n_ratio_average + 30.354 * n_ratio_average + 94.845
    else
        result.valid = 0
        result.spo2 = -1
    end

    --[[local ir_max=table_maxn(ir_a)
    local ir_min=table_minn(ir_a)
    local red_max=table_maxn(red_a)
    local red_min=table_minn(red_a)

    --log.info("spo2.ir",ir_min,ir_max)
    --log.info("spo2.red",red_min,red_max)

    local i_ir_ac=ir_max-ir_min
    local i_ir_dc=(ir_max+ir_min)/2
    local i_red_ac=red_max-red_min
    local i_red_dc=(red_max+red_min)/2

    local R=((i_red_ac)/(i_red_dc))/((i_ir_ac)/(i_ir_dc))

    log.info("spo2.R",R)

    result.spo2=(-45.060)*R*R+30.354*R+94.845]]

    log.debug("mem", collectgarbage("count"))
    -- ir_r=nil; red_r=nil; ir_p2=nil; red_p2=nil; ir_d=nil; red_d=nil;
    collectgarbage("collect")
    log.debug("mem", collectgarbage("count"))
    if network == 1 then
        sys.waitUntil('ir_d2', 1000)
    end
    return result
end

function find_max_peaks(y, x_size, n_min_height, n_min_distance, n_max_num)
    -- 找高度超过n_min_height的峰
    local i = 2
    local n_width
    local n_peaks = 0
    local peaks_locs = {}
    while i < x_size do
        if (y[i] > n_min_height) and (y[i] > y[i - 1]) then -- 找峰左边缘
            n_width = 1
            while (i + n_width <= x_size) and (y[i] == y[i + n_width]) do -- 把整个平顶延伸完加快后续查找
                n_width = n_width + 1
            end
            if ((i + n_width > x_size) or (y[i] > y[i + n_width])) and (n_peaks <= n_max_num * 3) then
                n_peaks = n_peaks + 1
                peaks_locs[n_peaks] = i -- 记录左边缘位置
                i = i + n_width + 1
            else
                i = i + n_width -- 跨过峰的半平腰继续找
            end
        else
            i = i + 1 -- 找下一个
        end
    end

    if (n_peaks >= 3) then
        local peak_interval_pre = peaks_locs[2] - peaks_locs[1]
        for i = 2, n_peaks - 1 do
            if ((peaks_locs[i + 1] - peaks_locs[i]) >= 3 * peak_interval_pre) then
                n_min_distance = math.max(n_min_distance, peak_interval_pre + 1)
            end
            if (3 * (peaks_locs[i + 1] - peaks_locs[i]) <= peak_interval_pre) then
                n_min_distance = math.max(n_min_distance, (peaks_locs[i + 1] - peaks_locs[i]) + 1)
            end
            peak_interval_pre = peaks_locs[i + 1] - peaks_locs[i]
        end
    end

    log.debug("find_max_peaks", x_size, y[x_size], ";;", n_peaks, n_min_distance, peaks_locs[n_peaks],
        peaks_locs[n_peaks - 1], peaks_locs[n_peaks - 2])

    -- 去除距离小于n_min_distance的峰（优先保留低高度峰）
    i = 1
    while i <= n_peaks - 1 do
        local tmax = y[peaks_locs[i]]
        local tpos = i
        local k = 0
        local j = i + 1
        while j <= n_peaks do
            if peaks_locs[j] > peaks_locs[i] + n_min_distance then
                k = j - 1
                break
            end
            if y[peaks_locs[j]] > tmax then
                tmax = y[peaks_locs[j]];
                tpos = j
            end
            j = j + 1
        end
        if (j > n_peaks) then
            k = n_peaks
        end
        for j = i + 1, k do
            if peaks_locs[i] == tpos then
                table.remove(peaks_locs, i + 1)
            else
                table.remove(peaks_locs, i)
            end
        end
        if k > i then
            n_peaks = n_peaks - (k - i)
        else
            i = i + 1
        end
    end

    if n_peaks > n_max_num then
        n_peaks = n_max_num
    end

    log.debug("find_max_peaks", x_size, y[x_size], ";;", n_peaks, peaks_locs[n_peaks], peaks_locs[n_peaks - 1],
        peaks_locs[n_peaks - 2])

    collectgarbage('collect')
    return n_peaks, peaks_locs
end

return max30102
